<?php

namespace App;

trait Upload {
    
    protected $imgMaxWidth = 750;
    protected $imgMaxHeight = 1200;
    
    /**
     * 分片上传
     */
    public function chunk() {
        
        if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
            $this->succ();
        }
        
        $tmpDir = DATA_PATH . '/Temp/chunk';
        
        if (!file_exists($tmpDir)) {
            @mkdir($tmpDir, 0775, true);
        }
        
        $cleanupTargetDir = true; //删除旧文件
        
        $maxFileAge = 5 * 3600; // 以秒为单位的临时文件大小
        
        if (isset($_REQUEST["name"])) {
            $fileName = md5($_REQUEST["name"] . $_REQUEST['lastModifiedDate']) . fileExt($_REQUEST["name"]);
            $size = $_REQUEST["size"];
        } elseif (!empty($_FILES)) {
            $fileName = md5($_FILES["file"]["name"] . $_REQUEST['lastModifiedDate']) . fileExt($_REQUEST["name"]);
            $size = $_FILES["file"]["size"];
        } else {
            $fileName = uniqid("file_");
        }
        
        $tempPath = $tmpDir . '/' . $fileName;
        
        // 获取组块
        $chunk = isset($_REQUEST["chunk"]) ? intval($_REQUEST["chunk"]) : 0;
        $chunks = isset($_REQUEST["chunks"]) ? intval($_REQUEST["chunks"]) : 1;
        
        // 删除旧的临时文件
        if ($cleanupTargetDir) {
            
            if (!is_dir($tmpDir) || !$dir = opendir($tmpDir)) {
                $this->fail('Failed to open temp directory.');
            }
            
            while (($file = readdir($dir)) !== false) {
                $tmpfilePath = $tmpDir . '/' . $file;
                
                // 如果临时文件是当前文件，则继续进行下一步
                if ($tmpfilePath == "{$tempPath}_{$chunk}.part" || $tmpfilePath == "{$tempPath}_{$chunk}.parttmp") {
                    continue;
                }
                
                // 如果临时文件比最大文件大，并且不是当前文件，则删除该文件
                if (preg_match('/\.(part|parttmp)$/', $file) && (@filemtime($tmpfilePath) < time() - $maxFileAge)) {
                    @unlink($tmpfilePath);
                }
            }
            
            closedir($dir);
        }
        
        // 打开临时文件
        if (!$out = @fopen("{$tempPath}_{$chunk}.parttmp", "wb")) {
            $this->fail('Failed to open output stream.');
        }
        
        if (!empty($_FILES)) {
            if ($_FILES["file"]["error"] || !is_uploaded_file($_FILES["file"]["tmp_name"])) {
                $this->fail('Failed to move uploaded file.');
            }
            
            // 读取二进制输入流并将其附加到临时文件
            if (!$in = @fopen($_FILES["file"]["tmp_name"], "rb")) {
                $this->fail('Failed to open input stream.');
            }
        } else {
            if (!$in = @fopen("php://input", "rb")) {
                $this->fail('Failed to open input stream.');
            }
        }
        
        while ($buff = fread($in, 4096)) {
            fwrite($out, $buff);
        }
        
        @fclose($out);
        @fclose($in);
        rename("{$tempPath}_{$chunk}.parttmp", "{$tempPath}_{$chunk}.part");
        
        $index = 0;
        $done = true;
        for ($index = 0; $index < $chunks; $index++) {
            if (!file_exists("{$tempPath}_{$index}.part")) {
                $done = false;
                break;
            }
        }
        
        if ($done) {
            
            if (!$out = @fopen($tempPath, "wb")) {
                $this->fail('Failed to open output stream.');
            }
            
            if (flock($out, LOCK_EX)) {
                for ($index = 0; $index < $chunks; $index++) {
                    if (!$in = @fopen("{$tempPath}_{$index}.part", "rb")) {
                        break;
                    }
                    while ($buff = fread($in, 4096)) {
                        fwrite($out, $buff);
                    }
                    @fclose($in);
                    @unlink("{$tempPath}_{$index}.part");
                }
                flock($out, LOCK_UN);
            }
            @fclose($out);
            
            Files()->setImgMaxSize($this->imgMaxWidth, $this->imgMaxHeight);
            
            $file_url = Files()->addFrom($tempPath);
            
            if (is_error($file_url)) {
                $this->fail(error_message($file_url));
            }
            
            $this->assign([
                'path' => $file_url,
                'sign' => $_REQUEST['sign'],
            ]);
            
            $this->succ(); // 上传完成
        }
        
        $this->succ(); // 分片完成
    }
    
    /**
     * 远程抓取文件
     */
    public function capture() {
        
        $raw = $this->request->get('url');
        
        $url = preg_replace('/(?:^[\'"]+|[\'"\/]+$)/', '', $raw); // 去除URL连接上面可能的引号
        
        $fileExt = fileExt($url);
        
        if (stripos($fileExt, '.png') === 0) {
            $fileExt = '.png';
        } else if (stripos($fileExt, '.jpg') === 0) {
            $fileExt = '.jpg';
        } else if (stripos($fileExt, '.gif') === 0) {
            $fileExt = '.gif';
        } else if (stripos($fileExt, '.webp') === 0) {
            $fileExt = '.webp';
        } else if (stripos($fileExt, '.awebp') === 0) {
            $fileExt = '.webp';
        } else {
            $fileExt = '.jpg';
        }
        
        $tmpDir = DATA_PATH . '/Temp/chunk';
        
        if (!file_exists($tmpDir)) {
            @mkdir($tmpDir, 0775, true);
        }
        
        $fileName = uniqid("file_");
        
        $tempPath = $tmpDir . '/' . $fileName;
        
        $p = parse_url($url);
        $host = $p['scheme'] . '://' . $p['host'];
        
        if (stripos($url, '.qlogo.cn') !== false || stripos($url, '.qpic.cn') !== false) {
            $header = array(
                "Host: mmbiz.qpic.cn",
                "Origin: https://mp.weixin.qq.com",
                "Referer: https://mp.weixin.qq.com/",
                "Sec-Fetch-Dest: image",
                "Sec-Fetch-Mode: cors",
                "Sec-Fetch-Site: cross-site",
                "TE: trailers",
                "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:101.0) Gecko/20100101 Firefox/101.0",
            );
        } else {
            $header = array(
                "Origin: {$host}",
                "Referer: {$host}",
                "Sec-Fetch-Dest: image",
                "Sec-Fetch-Mode: cors",
                "Sec-Fetch-Site: cross-site",
                "TE: trailers",
                "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:101.0) Gecko/20100101 Firefox/101.0",
            );
        }
        
        $ch = curl_init();
        $fp = fopen($tempPath, 'wb');
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_FILE, $fp);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
        curl_setopt($ch, CURLOPT_HEADER, 0);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
        curl_setopt($ch, CURLOPT_TIMEOUT, 60);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        $result = curl_exec($ch);
        curl_close($ch);
        fclose($fp);
        
        if (!$result) {
            $this->fail(curl_error($ch));
        }
        
        Files()->setImgMaxSize($this->imgMaxWidth, $this->imgMaxHeight);
        
        $file_url = Files()->addFrom($tempPath, '', $fileExt);
        
        if (is_error($file_url)) {
            $this->fail(error_message($file_url));
        }
        
        $this->assign([
            'path' => $file_url,
        ]);
        
        $this->succ(); // 上传完成
    }
    
    /**
     * base64图片上传
     */
    public function base64() {
        
        $dir = trim($this->request->get('dir')); // 上传文件目录
        
        $base64 = $this->request->getPost('base64'); // base64方式必须
        
        Files()->setImgMaxSize($this->imgMaxWidth, $this->imgMaxHeight);
        
        if ($dir && in_array($dir, $this->dirs)) {
            $file_url = Files()->addBase64Image($base64, $dir);
        } else {
            $file_url = Files()->addBase64Image($base64);
        }
        
        if (is_error($file_url)) {
            $this->fail(error_message($file_url));
        }
        
        $this->assign([
            'path' => $file_url,
            'raw' => substr($base64, 0, 120),
        ]);
        
        $this->succ(); // 上传完成
        
        $this->fail('unexpected error');
    }
    
}
